#include <pd_timer.h>
#include <hd_led.h>
#include <hd_oled.h>
#include <delay.h>
#include <hd_oled_protocol_spi.h>
#include <hd_oled_protocol_i2c.h>
#include <pd_timer_oc.h>
#include <pd_timer_pwm.h>
#include <pd_i2c_hd.h>
#include "main.h"

#define ARRAY_SIZE(x)       sizeof(x) / sizeof(x[0])
#define USE_HARDWARE_SPI    1
#define USE_HARDWARE_I2C    1
OLED oled;

extern void SystemClock_Config(void);

void oled_instance(OLED *oled);

int main(void) {
    SystemClock_Config();

    NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);

    oled_instance(&oled);

    OLED_printf(&oled, 0, 0, "TIM PWM LED!");
    OLED_printf(&oled, 0, 16, "GPIOB 6/7/8/9");
    OLED_refresh(&oled);

    // PB6 -- TIM4_CH1; PB7 -- TIM4_CH2; PB8 -- TIM4_CH3; PB9 -- TIM4_CH4
#ifdef MORE_CHANNEL
    GPIO gpios[4];
    GPIO_constructor(gpios + 0, GPIOB, LL_GPIO_PIN_6);
    (gpios + 0)->Mode = LL_GPIO_MODE_ALTERNATE;
    (gpios + 0)->Alternate = LL_GPIO_AF_2;

    GPIO_constructor(gpios + 1, GPIOB, LL_GPIO_PIN_7);

    (gpios + 1)->Mode = LL_GPIO_MODE_ALTERNATE;
    (gpios + 1)->Alternate = LL_GPIO_AF_2;

    GPIO_constructor(gpios + 2, GPIOB, LL_GPIO_PIN_8);
    (gpios + 2)->Mode = LL_GPIO_MODE_ALTERNATE;
    (gpios + 2)->Alternate = LL_GPIO_AF_2;

    GPIO_constructor(gpios + 3, GPIOB, LL_GPIO_PIN_9);
    (gpios + 3)->Mode = LL_GPIO_MODE_ALTERNATE;
    (gpios + 3)->Alternate = LL_GPIO_AF_2;
#else
    GPIO gpios[1];
    GPIO_constructor(gpios, GPIOB, LL_GPIO_PIN_6 | LL_GPIO_PIN_7 | LL_GPIO_PIN_8 | LL_GPIO_PIN_9);
    gpios->Mode = LL_GPIO_MODE_ALTERNATE;
    gpios->Alternate = LL_GPIO_AF_2;
#endif
    uint32_t channel[] = {LL_TIM_CHANNEL_CH1, LL_TIM_CHANNEL_CH2, LL_TIM_CHANNEL_CH3, LL_TIM_CHANNEL_CH4};
    TimerCh timerChannel;
    TimerCh_constructor(&timerChannel, gpios, ARRAY_SIZE(gpios), channel, ARRAY_SIZE(channel));

    // freq = 1kHz, duty is variable, resolution = 1%
    Timer timer;
    Timer_constructor(&timer, TIM4, 840 - 1, 100 - 1);

    TimerOC timerOC;
    TimerOC_constructor(&timerOC, &timer, &timerChannel, 0);

    TimerPWM timerPWM;
    TimerPWM_constructor(&timerPWM, &timer, &timerOC);
    TimerPWM_init(&timerPWM);

    while (1) {
        for (int i = 0; i < 100; ++i) {
            TimerPWM_setDuty(&timerPWM, LL_TIM_CHANNEL_CH1, i);
            TimerPWM_setDuty(&timerPWM, LL_TIM_CHANNEL_CH2, 100 - i);
            TimerPWM_setDuty(&timerPWM, LL_TIM_CHANNEL_CH3, i);
            TimerPWM_setDuty(&timerPWM, LL_TIM_CHANNEL_CH4, 100 - i);
            delay_ms(10);
        }

        delay_ms(100);

        for (int i = 0; i < 100; ++i) {
            TimerPWM_setDuty(&timerPWM, LL_TIM_CHANNEL_CH1, 100 - i);
            TimerPWM_setDuty(&timerPWM, LL_TIM_CHANNEL_CH2, i);
            TimerPWM_setDuty(&timerPWM, LL_TIM_CHANNEL_CH3, 100 - i);
            TimerPWM_setDuty(&timerPWM, LL_TIM_CHANNEL_CH4, i);
            delay_ms(10);
        }

        delay_ms(100);
    }
}

void oled_instance(OLED *oled) {
    // I2C gpio init
    static GPIO oledSckGPIO, oledSdaGPIO;
    GPIO_constructor(&oledSckGPIO, GPIOB, LL_GPIO_PIN_10); // I2Cx_SCK
    GPIO_constructor(&oledSdaGPIO, GPIOB, LL_GPIO_PIN_11); // I2Cx_SDA

#ifdef USE_HARDWARE_I2C
    static I2CHardware i2c2;
    I2CHardware_constructor(&i2c2, I2C2, &oledSckGPIO, &oledSdaGPIO);
#elif USE_SIMULATE_I2C
    static I2CSimulate i2c2;
    I2CSimulate_constructor(&i2c2, &oledSckGPIO, &oledSdaGPIO);
#endif
    I2C_init((I2C *)&i2c2);

    // OLED protocol of I2C
    static OLEDProtocolI2C oledProtocolI2c;
    OLEDProtocolI2C_constructor(&oledProtocolI2c, (I2C *)&i2c2);

    // OLED font instance.
    static OLEDFont oledFont;
    OLEDFont_contructor(&oledFont, NULL);

    // OLED instance of 4-PIN(I2C)
    OLED_constructor(oled, (OLEDProtocol *)&oledProtocolI2c, &oledFont);

    OLED_init(oled);

    OLED_clear(oled);
    OLED_refresh(oled);
}
